Tasklet bug fixes.
authorKeir Fraser <keir.fraser@citrix.com>
Tue, 15 Apr 2008 14:03:40 +0000 (15:03 +0100)
committerKeir Fraser <keir.fraser@citrix.com>
Tue, 15 Apr 2008 14:03:40 +0000 (15:03 +0100)
Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
xen/common/softirq.c
xen/include/xen/softirq.h

index 1888d1711280fe5604725299ba8b63a07a3df5b4..be4728f2e07534505c18f31ce30e05485174dfe5 100644 (file)
@@ -61,15 +61,18 @@ void tasklet_schedule(struct tasklet *t)
 
     spin_lock_irqsave(&tasklet_lock, flags);
 
-    if ( !t->is_scheduled )
+    if ( !t->is_dead )
     {
-        list_add(&t->list, &tasklet_list);
+        if ( !t->is_scheduled && !t->is_running )
+        {
+            BUG_ON(!list_empty(&t->list));
+            list_add_tail(&t->list, &tasklet_list);
+        }
         t->is_scheduled = 1;
+        raise_softirq(TASKLET_SOFTIRQ);
     }
 
     spin_unlock_irqrestore(&tasklet_lock, flags);
-
-    raise_softirq(TASKLET_SOFTIRQ);
 }
 
 static void tasklet_action(void)
@@ -78,24 +81,38 @@ static void tasklet_action(void)
 
     spin_lock_irq(&tasklet_lock);
 
-    while ( !list_empty(&tasklet_list) )
+    if ( list_empty(&tasklet_list) )
     {
-        t = list_entry(tasklet_list.next, struct tasklet, list);
-        list_del(&t->list);
+        spin_unlock_irq(&tasklet_lock);
+        return;
+    }
+
+    t = list_entry(tasklet_list.next, struct tasklet, list);
+    list_del_init(&t->list);
 
-        BUG_ON(!t->is_scheduled);
-        t->is_scheduled = 0;
+    BUG_ON(t->is_dead || t->is_running || !t->is_scheduled);
+    t->is_scheduled = 0;
+    t->is_running = 1;
 
-        BUG_ON(t->is_running);
-        t->is_running = 1;
+    spin_unlock_irq(&tasklet_lock);
+    t->func(t->data);
+    spin_lock_irq(&tasklet_lock);
 
-        spin_unlock_irq(&tasklet_lock);
-        t->func(t->data);
-        spin_lock_irq(&tasklet_lock);
+    t->is_running = 0;
 
-        t->is_running = 0;
+    if ( t->is_scheduled )
+    {
+        BUG_ON(t->is_dead || !list_empty(&t->list));
+        list_add_tail(&t->list, &tasklet_list);
     }
 
+    /*
+     * If there is more work to do then reschedule. We don't grab more work
+     * immediately as we want to allow other softirq work to happen first.
+     */
+    if ( !list_empty(&tasklet_list) )
+        raise_softirq(TASKLET_SOFTIRQ);
+
     spin_unlock_irq(&tasklet_lock);
 }
 
@@ -105,12 +122,14 @@ void tasklet_kill(struct tasklet *t)
 
     spin_lock_irqsave(&tasklet_lock, flags);
 
-    /* De-schedule the tasklet and prevent it from re-scheduling itself. */
     if ( !list_empty(&t->list) )
-        list_del(&t->list);
-    t->is_scheduled = 1;
+    {
+        BUG_ON(t->is_dead || t->is_running || !t->is_scheduled);
+        list_del_init(&t->list);
+    }
+    t->is_scheduled = 0;
+    t->is_dead = 1;
 
-    /* Wait for tasklet to complete. */
     while ( t->is_running )
     {
         spin_unlock_irqrestore(&tasklet_lock, flags);
@@ -118,9 +137,6 @@ void tasklet_kill(struct tasklet *t)
         spin_lock_irqsave(&tasklet_lock, flags);
     }
 
-    /* Clean up and we're done. */
-    t->is_scheduled = 0;
-
     spin_unlock_irqrestore(&tasklet_lock, flags);
 }
 
@@ -128,6 +144,7 @@ void tasklet_init(
     struct tasklet *t, void (*func)(unsigned long), unsigned long data)
 {
     memset(t, 0, sizeof(*t));
+    INIT_LIST_HEAD(&t->list);
     t->func = func;
     t->data = data;
 }
index 4fa7a39b0cbe713626fb00f2c96f355e125e4975..0b59f63b2224f626e9772d221bb11003ad54818d 100644 (file)
@@ -62,12 +62,13 @@ struct tasklet
     struct list_head list;
     bool_t is_scheduled;
     bool_t is_running;
+    bool_t is_dead;
     void (*func)(unsigned long);
     unsigned long data;
 };
 
 #define DECLARE_TASKLET(name, func, data) \
-    struct tasklet name = { LIST_HEAD_INIT(name.list), 0, 0, func, data }
+    struct tasklet name = { LIST_HEAD_INIT(name.list), 0, 0, 0, func, data }
 
 void tasklet_schedule(struct tasklet *t);
 void tasklet_kill(struct tasklet *t);